package com.naico.core.service;

import com.naico.core.pojo.*;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;

import java.awt.*;
import java.util.*;
import java.util.List;

/**
 * User: Dawood Ajaz
 * Date: Jan 16, 2009
 * Time: 6:53:52 PM
 */
public class ChartGeneratorService {

    /**
     * Creates a bar chart
     */
    public ChartGeneratorService() {
    }

    /**
     * Creates a dataset.
     *
     * @return a dataset.
     */
    public CategoryDataset createDataset(java.util.List<PaymentPlan> paymentPlans) {

        ActualPaymentPlan actualPaymentPlan = (ActualPaymentPlan) paymentPlans.get(0);
        RollDownPaymentPlan rollDownPaymentPlan = (RollDownPaymentPlan) paymentPlans.get(1);

        List<PaymentSchedule> actualPaymentSchedules = actualPaymentPlan.getPaymentSchedules();
        List<PaymentSchedule> rollDownPaymentSchedules = rollDownPaymentPlan.getPaymentSchedules();

        Map<String, Double> actualYearlyEndingBalance =
                getYearlyTotalEndingBalance(actualPaymentPlan, actualPaymentSchedules);
        Map<String, Double> rollDownYearlyEndingBalance =
                getYearlyTotalEndingBalance(rollDownPaymentPlan, rollDownPaymentSchedules);

        // create the dataset...
        DefaultCategoryDataset dataset = new DefaultCategoryDataset();

        // row keys...
        String actualPaymentPlanSeries = "Actual Payment Plan";
        String rolldownPaymentPlanSeries = "Roll Down Payment Plan";

        // generate series for each payment plan
        generateSeries(actualPaymentPlanSeries, actualYearlyEndingBalance, dataset);
        generateSeries(rolldownPaymentPlanSeries, rollDownYearlyEndingBalance, dataset);

        return dataset;
    }

    /**
     * Creates a 3D bar chart.
     *
     * @param dataset the dataset.
     * @return a 3D bar chart.
     */
    public JFreeChart createChart(final CategoryDataset dataset) {

        final JFreeChart chart = ChartFactory.createAreaChart(
                "Yearly Total Debt Balance",      // chart title
                "Years",                          // domain axis label
                "Remaining Balance",              // range axis label
                dataset,                          // data
                PlotOrientation.VERTICAL,         // the plot orientation
                true,                             // include legend
                true,                             // tooltips?
                false                             // URLs?
        );

        chart.setBackgroundPaint(Color.white);
        chart.setBorderVisible(false);

        // get a reference to the plot for further customisation...
        final CategoryPlot plot = chart.getCategoryPlot();
        plot.setNoDataMessage("NO DATA!");
        plot.setBackgroundPaint(Color.lightGray);
        plot.setDomainGridlinePaint(Color.white);
        plot.setForegroundAlpha(0.4f);
        plot.setDomainGridlinesVisible(true);
        plot.setRangeGridlinePaint(Color.white);

        CategoryAxis axis = plot.getDomainAxis();
        axis.setLowerMargin(-0.05);

        return chart;
    }


    private Map<String, Double> getYearlyTotalEndingBalance(PaymentPlan plan, List<PaymentSchedule> paymentSchedules) {
        // using LinkedHashMap to keep the same order in which keys were inserted into the map
        Map<String, Double> yearlyEndingBalanceMap = new LinkedHashMap<String, Double>();

        yearlyEndingBalanceMap.put("0", plan.getTotalDebtAmount());

        for (PaymentSchedule paymentSchedule : paymentSchedules) {
            List<PaymentScheduleItem> items = paymentSchedule.getPaymentScheduleItems();
            Map<String, Double> perItemBalanceMap = new LinkedHashMap<String, Double>();

            for (PaymentScheduleItem item : items) {
                Date date = item.getMonth();
                Calendar cal = Calendar.getInstance();
                cal.setTime(date);

                int year = cal.get(Calendar.YEAR);
                int month = cal.get(Calendar.MONTH) + 1;

                // following would store Quarterly balances
                if (month % 3 == 0) {
                    String qtr = String.valueOf(month) + "/";
                    qtr += String.valueOf(year).substring(2);
                    perItemBalanceMap.put(qtr, item.getEndingBalance());
                }
            }

            // if it was the first schedule then just add all to output map
            if (yearlyEndingBalanceMap.size() == 0) {
                yearlyEndingBalanceMap.putAll(perItemBalanceMap);
            } else {
                // add the years total already in map
                for (String qtr : perItemBalanceMap.keySet()) {
                    // if the key in perItemMap exists then add value with the existing one
                    if (yearlyEndingBalanceMap.containsKey(qtr)) {
                        Double val = yearlyEndingBalanceMap.get(qtr);
                        Double accum = perItemBalanceMap.get(qtr) + val;
                        yearlyEndingBalanceMap.put(qtr, accum);
                    } else { // else just add a new balance value
                        yearlyEndingBalanceMap.put(qtr, perItemBalanceMap.get(qtr));
                    }
                }
            }
        }

        return yearlyEndingBalanceMap;
    }

    private Map<Integer, Double> getMontlyTotalEndingBalance(List<PaymentSchedule> paymentSchedules) {
        // using LinkedHashMap to keep the same order in which keys were inserted into the map
        Map<Integer, Double> monthlyEndingBalanceMap = new LinkedHashMap<Integer, Double>();

        for (PaymentSchedule paymentSchedule : paymentSchedules) {
            List<PaymentScheduleItem> items = paymentSchedule.getPaymentScheduleItems();
            Map<Integer, Double> perItemBalanceMap = new LinkedHashMap<Integer, Double>();

            for (PaymentScheduleItem item : items) {
                Date date = item.getMonth();
                Calendar cal = Calendar.getInstance();
                cal.setTime(date);

                int year = cal.get(Calendar.YEAR);
                // just update or replace with the latest ending balance
                perItemBalanceMap.put(year, item.getEndingBalance());
            }

            // if it was the first schedule then just add all to output map
            if (monthlyEndingBalanceMap.size() == 0) {
                monthlyEndingBalanceMap.putAll(perItemBalanceMap);
            } else {
                // add the years total already in map
                for (Integer year : perItemBalanceMap.keySet()) {
                    // if the key in perItemMap exists then add value with the existing one
                    if (monthlyEndingBalanceMap.containsKey(year)) {
                        Double val = monthlyEndingBalanceMap.get(year);
                        Double accum = perItemBalanceMap.get(year) + val;
                        monthlyEndingBalanceMap.put(year, accum);
                    } else { // else just add a new balance value
                        monthlyEndingBalanceMap.put(year, perItemBalanceMap.get(year));
                    }
                }
            }
        }

        return monthlyEndingBalanceMap;
    }


    private static void generateSeries(String series,
                                       Map<String, Double> yearlyEndBalance,
                                       DefaultCategoryDataset dataset) {
        int year = 0;
        // actual payment series
        Set<Map.Entry<String, Double>> entries = yearlyEndBalance.entrySet();
        for (Map.Entry<String, Double> entry : entries) {
            Double value = entry.getValue();
            String key = entry.getKey();
            dataset.addValue(value, series, key);
        }
    }
}